package com.pig4cloud.pig.auth.support.sms;

import com.pig4cloud.pig.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationConverter;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.Set;

/**
 * OAuth2 资源所有者短信认证转换器
 * <p>
 * 该类继承自 OAuth2ResourceOwnerBaseAuthenticationConverter，
 * 负责将 HTTP 请求转换为短信验证码模式的认证令牌。
 * 
 * 主要功能：
 * 1. 验证请求中的手机号参数
 * 2. 构建短信认证模式的认证令牌
 * 3. 确保必须参数的存在和唯一性
 * 
 * 自定义授权类型：
 * - grant_type=mobile
 * - mobile：必须参数，手机号
 * - code：必须参数，短信验证码（在过滤器中验证）
 * - scope：可选参数，访问范围
 * 
 * @author lengleng
 * @date 2022-05-31
 */
public class OAuth2ResourceOwnerSmsAuthenticationConverter
		extends OAuth2ResourceOwnerBaseAuthenticationConverter<OAuth2ResourceOwnerSmsAuthenticationToken> {

	/**
	 * 判断是否支持指定的授权类型
	 * <p>
	 * 该方法仅支持短信验证码模式（mobile）。
	 * 当请求中的 grant_type 参数为 "mobile" 时返回 true。
	 * 
	 * @param grantType 授权类型
	 * @return 如果是短信验证码模式返回 true，否则返回 false
	 */
	@Override
	public boolean support(String grantType) {
		return SecurityConstants.MOBILE.equals(grantType);
	}

	/**
	 * 构建 OAuth2 资源所有者短信认证令牌
	 * <p>
	 * 使用提供的参数创建一个新的短信认证令牌。
	 * 这个令牌将在后续的认证流程中被 Provider 处理。
	 * 
	 * @param clientPrincipal 客户端主体认证信息，包含已认证的客户端信息
	 * @param requestedScopes 请求的作用域集合，定义访问权限范围
	 * @param additionalParameters 附加参数映射，包含 mobile、code 等信息
	 * @return 构建完成的 OAuth2 资源所有者短信认证令牌
	 */

	/**
	 * 校验扩展参数
	 * <p>
	 * 短信验证码模式必须提供以下参数：
	 * 1. mobile：手机号（必须）
	 * 2. code：短信验证码（在过滤器中校验）
	 * 
	 * 校验规则：
	 * - 手机号不能为空
	 * - 手机号只能出现一次（防止参数注入）
	 * - 如果校验失败，抛出 OAuth2 错误
	 * 
	 * 注意：验证码的校验在 ValidateCodeFilter 中完成
	 * 
	 * @param request HTTP 请求对象，包含所有请求参数
	 * @throws OAuth2AuthenticationException 当参数缺失或无效时抛出
	 */
	@Override
	public void checkParams(HttpServletRequest request) {
		// 获取请求参数
		MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
		
		// 校验手机号参数（必须）
		// 手机号不能为空且只能出现一次
		String phone = parameters.getFirst(SecurityConstants.SMS_PARAMETER_NAME);
		if (!StringUtils.hasText(phone) || parameters.get(SecurityConstants.SMS_PARAMETER_NAME).size() != 1) {
			OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, SecurityConstants.SMS_PARAMETER_NAME,
					OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
		}
	}

}
